package utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;

import javax.persistence.NoResultException;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import model.City;
import model.Flight;
import model.Passenger;
import model.User;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

/**
 * Repository manager i session listener
 * Videti: web.xml i applicationContext.xml
 */
public class AirTransportRepositoryManager implements HttpSessionListener {

	private static final int MAX_SESSION_ACTIVE_INTERVAL = 300;
	
	
	public void sessionCreated(HttpSessionEvent arg0) {
		arg0.getSession().setMaxInactiveInterval(MAX_SESSION_ACTIVE_INTERVAL);
		System.out.println("SESSION CREATED: " + arg0.getSession().getId());
	}
	
	
	public void sessionDestroyed(HttpSessionEvent arg0) {
		User user = (User) arg0.getSession().getAttribute("user");
		if (user != null) {
			logoutUser(user);
		}
		System.out.println("SESSION ENDED: " + arg0.getSession().getId());
	}
	
	
	/**
	 * Entity manager za interakciju sa modelom.
	 */
	private Session entityManager;
	private Transaction transaction;
	
	/**
	 * Cuva listu logovanih korisnika.
	 */
	private static HashMap<String, User> loggedUsers;
	
	
	static {
		loggedUsers = new HashMap<String, User>();
	}
	
	
	public AirTransportRepositoryManager() {
		entityManager = HibernateUtil.getSession();
	}
	
	
	/**
	 * Detektuje da li je user logovan.
	 * @param user - user za kog se proverava da li je logovan
	 * @return true, ako je logovan, inace false
	 */
	public static boolean isUserLoggedIn(User user) {
		synchronized (loggedUsers) {
			return loggedUsers.containsKey(user.getUsername());
		}
	}
	
	
	public boolean registerUser(User user) {
		
		transaction = entityManager.beginTransaction();
		
		// user existance test
		Query query = entityManager.getNamedQuery("user_by_username");
		query.setParameter("username", user.getUsername());
		User tempUser = null;
		
		try {
			tempUser = (User) query.uniqueResult();
		}
		catch (NoResultException nre) {
			tempUser = null;
		}

		
		// proverava se da li korisnik postoji
		// ukoliko postoje, prijavljuje se greska
		if (tempUser != null) {
			return false;
		}
		
		// registrovanje korisnika

		try {
			entityManager.persist(user);
			transaction.commit();
		}
		catch (Exception e) {
			transaction.rollback();
			return false;
		}

		return true;
	}
	
	
	
	public boolean loginUser(User user) {
		
		// user existance test
		Query query = entityManager.getNamedQuery("user_by_username");
		query.setParameter("username", user.getUsername());
		User tempUser = null;
		
		try {
			tempUser = (User) query.uniqueResult();
		}
		catch (NoResultException nre) {
			return false;
		}
		
		// proverava se da li korisnik postoji, vraca se false
		if (tempUser == null) {
			return false;
		}		
		
		// provera lozinke
		if ( !user.getPassword().equals(tempUser.getPassword()) ) {
			return false;
		}
		
		synchronized (loggedUsers) {
			if ( loggedUsers.get(user.getUsername()) != null ) {
				return false;
			}
		}

		synchronized (loggedUsers) {
			loggedUsers.put(user.getUsername(), user);
		}
		
		return true;
	}
	
	
	
	public boolean logoutUser(User user) {
		
		synchronized (loggedUsers) {
			loggedUsers.remove(user.getUsername());
		}
		
		return true;
	}
	
	
	@SuppressWarnings("unchecked")
	public Vector<Flight> getFlights() {
		
		Query query = entityManager.getNamedQuery("all_flights");
		List<Flight> flights;
		
		try {
			flights = query.list();
		}
		catch (NoResultException nre) {
			return null;
		}
		
		Vector<Flight> temp = new Vector<Flight>(flights);
		
		return temp;
	}
	
	
	public boolean addNewCity(String cityID, String cityName) {
		transaction = entityManager.beginTransaction();
		
		Query query = entityManager.getNamedQuery("city_by_id");
		query.setParameter("id", cityID);
		City city = null;
		
		try {
			city = (City) query.uniqueResult();
		}
		catch (NoResultException nre) {
			city = null;
		}
		
		if (city != null) {
			return false;
		}
		
		city = new City(cityID, cityName);
		
		try {
			entityManager.persist(city);
			transaction.commit();
		}
		catch (Exception e) {
			transaction.rollback();
			return false;
		}
		
		return true;
	}
	
	
	@SuppressWarnings("unchecked")
	public Vector<City> getCities() {
		
		Query query = entityManager.getNamedQuery("all_cities");
		List<City> cities;
		
		try {
			cities = query.list();
		}
		catch (NoResultException nre) {
			return null;
		}
		
		Vector<City> temp = new Vector<City>(cities);
		
		return temp;
	}
	
	
	public boolean addNewFlight(String flightID, String depTime, String arrTime, String srcCityID, String destCityID, String msc) {
		transaction = entityManager.beginTransaction();
		
		Query query = entityManager.getNamedQuery("flight_by_id");
		query.setParameter("id", flightID);
		Flight flight = null;
		
		try {
			flight = (Flight) query.uniqueResult();
		}
		catch (NoResultException nre) {
			flight = null;
		}
		
		if (flight != null) {
			return false;
		}
		
		SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy. HH:mm");
		
		Date departureTime = null;
		try {
			departureTime = sdf.parse(depTime);
		}
		catch (ParseException e) {
			return false;
		}
		
		Date arrivalTime = null;
		try {
			arrivalTime = sdf.parse(arrTime);
		}
		catch (ParseException e) {
			return false;
		}
		
		City srcCity = null;
		query = entityManager.getNamedQuery("city_by_id");
		query.setParameter("id", srcCityID);
		try {
			srcCity = (City) query.uniqueResult();
		}
		catch (NoResultException nre) {
			return false;
		}
		
		City destCity = null;
		query = entityManager.getNamedQuery("city_by_id");
		query.setParameter("id", destCityID);
		try {
			destCity = (City) query.uniqueResult();
		}
		catch (NoResultException nre) {
			return false;
		}
		
		int maxSeatsCount = 0;
		
		try {
			maxSeatsCount = Integer.parseInt(msc);
		}
		catch (NumberFormatException nfe) {
			return false;
		}
		
		if (maxSeatsCount <= 0) {
			return false;
		}
		
		flight = new Flight(flightID, departureTime, arrivalTime, srcCity, destCity, maxSeatsCount);
		
		try {
			entityManager.persist(flight);
			transaction.commit();
		}
		catch (Exception e) {
			transaction.rollback();
			return false;
		}

		return true;
	}
	
	
	public Flight getFlightByID(String flightID) {
		Query query = entityManager.getNamedQuery("flight_by_id");
		query.setParameter("id", flightID);
		Flight flight = null;
		
		try {
			flight = (Flight) query.uniqueResult();
		}
		catch (NoResultException nre) {
			flight = null;
		}
		
		return flight;
	}
	
	
	public Vector<Passenger> getPassengersByFlight(String flightID) {
		Query query = entityManager.getNamedQuery("flight_by_id");
		query.setParameter("id", flightID);
		Flight flight = null;
		
		try {
			flight = (Flight) query.uniqueResult();
		}
		catch (NoResultException nre) {
			return null;
		}
		
		return new Vector<Passenger>(flight.getPassengers());
	}
	
	
	public int reserve(String flightID, String jmbg, String name, String surname) {
		transaction = entityManager.beginTransaction();
		
		Query query = entityManager.getNamedQuery("flight_by_id");
		query.setParameter("id", flightID);
		Flight flight = null;
		
		try {
			flight = (Flight) query.uniqueResult();
		}
		catch (NoResultException nre) {
			return 0;
		}
		
		// ne dozvoljava se rezervacija ako se zahtev uputi kasnije od jedan sat pre poletanja
		if (ReservationDateTest.testDate(flight.getDepartureTime()) == true) {
			return 1;
		}
		
		
		if (flight.hasFreeSeats() == false) {
			return 2;
		}
		
		
		if (jmbg.length() != 13) {
			return 3;
		}
		
		long jmbgNo = 0;
		
		try {
			jmbgNo = Long.parseLong(jmbg);
		}
		catch (NumberFormatException nfe) {
			return 4;
		}
		
		
		query = entityManager.getNamedQuery("passenger_by_jmbg");
		query.setParameter("jmbg", new Long(jmbgNo));
		Passenger passenger = null;
		
		try {
			passenger = (Passenger) query.uniqueResult();
		}
		catch (NoResultException nre) {
			passenger = null;
		}
		
		
		if (passenger == null) {
			passenger = new Passenger(jmbgNo, name, surname);
			flight.reserveSeat(passenger);
			passenger.addToFlight(flight);
			
			try {
				entityManager.persist(passenger);
				entityManager.merge(flight);
				transaction.commit();
			}
			catch (Exception e) {
				transaction.rollback();
			}
		}
		else {
			if (flight.getPassengers().contains(passenger)) {
				// putnik je vec rezervisao sediste na ovom letu
				return 5;
			}
			else {
				// provera da li se vreme poklapa sa nekim drugim letom
				for (Flight f : passenger.getFlights()) {
					if ( isIntersected(flight, f) ) {
						return 6;
					}
				}
				
				// rezeravcija
				flight.reserveSeat(passenger);
				passenger.addToFlight(flight);
				
				try {
					entityManager.merge(passenger);
					entityManager.merge(flight);
					transaction.commit();
				}
				catch (Exception e) {
					transaction.rollback();
				}
			}
		}

		return 7;
	}
	
	
	private boolean isIntersected(Flight f1, Flight f2) {
		
		Date depF1 = f1.getDepartureTime();
		Date arrF1 = f1.getArrivalTime();
		Date depF2 = f2.getDepartureTime();
		Date arrF2 = f2.getArrivalTime();
		
		if (depF1.after(depF2) && arrF1.before(arrF2)) {
			return true;
		}
		
		if (depF2.after(depF1) && arrF2.before(arrF1)) {
			return true;
		}
		
		if (depF1.after(depF2) && depF1.before(arrF2)) {
			return true;
		}
		
		if (arrF1.after(depF2) && arrF1.before(arrF2)) {
			return true;
		}
		
		return false;
	}
	
	
	public Vector<Flight> getFlightsByPassenger(String jmbg) {
		Query query = entityManager.getNamedQuery("passenger_by_jmbg");
		query.setParameter("jmbg", new Long(jmbg));
		Passenger passenger = null;
		
		try {
			passenger = (Passenger) query.uniqueResult();
		}
		catch (NoResultException nre) {
			return null;
		}
		
		return new Vector<Flight>(passenger.getFlights());
	}
	
	
	public Passenger getPassengerByJMBG(String jmbg) {
		Query query = entityManager.getNamedQuery("passenger_by_jmbg");
		query.setParameter("jmbg", new Long(jmbg));
		Passenger passenger = null;
		
		try {
			passenger = (Passenger) query.uniqueResult();
		}
		catch (NoResultException nre) {
			passenger = null;
		}
		
		return passenger;
	}
	
	
	public boolean cancel(String flightID, String jmbg) {
		transaction = entityManager.beginTransaction();
		
		Query query = entityManager.getNamedQuery("passenger_by_jmbg");
		query.setParameter("jmbg", new Long(jmbg));
		Passenger passenger = null;
		
		try {
			passenger = (Passenger) query.uniqueResult();
		}
		catch (NoResultException nre) {
			return false;
		}
		
		query = entityManager.getNamedQuery("flight_by_id");
		query.setParameter("id", flightID);
		Flight flight = null;
		
		try {
			flight = (Flight) query.uniqueResult();
		}
		catch (NoResultException nre) {
			return false;
		}
		
		if (passenger == null) {
			return false;
		}
		
		passenger.getFlights().remove(flight);
		flight.unreserveSeat(passenger);
		
		try {
			if (passenger.getFlights().size() == 0) {
				entityManager.delete(passenger);
			}
			else {
				entityManager.merge(passenger);
			}
		
			entityManager.merge(flight);
			
			transaction.commit();
		}
		catch (Exception e) {
			transaction.rollback();
			return false;
		}
		
		return true;
	}
	
}
